package util;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Stroke;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.swing.JPanel;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import common.Static;
import optimizers.commons.RunConfig;
import optimizers.ganeat.Constants;
import optimizers.ganeat.Individual;
import optimizers.ganeat.Species;
import optimizers.ganeat.fcd.ConstantsFCD;
import optimizers.ganeat.math.ConstantsMath;
import visualization.PlotExpData;

public class MyUtil {
	public static int windowSizeForPeakSearching = 15;

	/*
	 * public static String[] seqStr = new String[] { "CTGAGTCGGAT",
	 * "TAGAGTCTGCC", "CTGAGTCGGTG", "CAGAGTCGAGG", "GCGAGTCGCTA",
	 * "TAGAGTCGTCA", "TCGAGTCGGAA", "GAGAGTCTATA", "ATGAGTCCCAA",
	 * "CAGAGTCGATT", "GTGAGTCCTTA", "ACGAGTCCATA", "TGGAGTCACAG",
	 * "GCGAGTCTTAT", "CTGAGTCCCTT" }; public static double[] seqK = new
	 * double[] { 191.41, 277.91, 61.76, 99.56, 3.08, 612.4, 53.2, 21987.17,
	 * 389.02, 739.65, 1532.11, 478.89, 191.41, 666.12, 261.65 };
	 */
	/*
	 * public static String[] seqStr = new String[] { "CTGAGTCGGAT",
	 * "TAGAGTCTGCC", "CTGAGTCGGTG", "CAGAGTCGAGG", "GCGAGTCGCTA",
	 * "TAGAGTCGTCA", "TCGAGTCGGAA", "ATGAGTCCCAA", "CAGAGTCGATT",
	 * "ACGAGTCCATA", "TGGAGTCACAG", "GCGAGTCTTAT", "CTGAGTCCCTT" }; public
	 * static double[] seqK = new double[] { 191.41, 277.91, 61.76, 99.56, 3.08,
	 * 612.4, 53.2, 389.02, 739.65, 478.89, 191.41, 666.12, 261.65 };
	 */

	/*
	 * public static String[] seqStr = new String[] { "CCGAGTCAACA",
	 * "CTGAGTCGCGT", "ATGAGTCGTGC", "GCGAGTCCATT", "CGGAGTCCTGT",
	 * "AGGAGTCCGAA", "GGGAGTCACAG", "GCGAGTCAGTT", "GCGAGTCGTAA",
	 * "GCGAGTCTCAA", "CGGAGTCGAAT", "CGGAGTCCCTA" }; public static double[]
	 * seqK = new double[] { 99.72, 11.49, 65.29, 69.01, 33.32, 86.59, 98.46,
	 * 70.79, 71.24, 88.25, 99.56, 68.14 };
	 */

	public static String numberToGreeceString(int i) {
		switch (i) {
		case 1:
			return "\u03B1";
		case 2:
			return "\u03B2";
		case 3:
			return "\u03B3";
		case 4:
			return "\u03B4";
		case 5:
			return "\u03B5";
		case 6:
			return "\u03B6";
		case 7:
			return "\u03B7";
		case 8:
			return "\u03B8";
		case 9:
			return "\u03B9";
		case 10:
			return "\u03BA";
		default:
			return String.valueOf(i);
		}
	}

	public static String numberToString(int i) {
		switch (i) {
		case 1:
			return "a";
		case 2:
			return "b";
		case 3:
			return "c";
		case 4:
			return "d";
		case 5:
			return "e";
		case 6:
			return "f";
		case 7:
			return "g";
		case 8:
			return "h";
		case 9:
			return "i";
		case 10:
			return "j";
		default:
			return String.valueOf(i);
		}
	}

	public static void multiArrayCopy(double[][] source, double[][] destination) {
		for (int a = 0; a < source.length; a++) {
			System.arraycopy(source[a], 0, destination[a], 0, source[a].length);
		}
	}

	public static void multiArrayCopy(boolean[][] source,
			boolean[][] destination) {
		for (int a = 0; a < source.length; a++) {
			System.arraycopy(source[a], 0, destination[a], 0, source[a].length);
		}
	}

	public static void multiArrayCopy(boolean[][][] source,
			boolean[][][] destination) {
		for (int a = 0; a < source.length; a++) {
			boolean[][] tmp = source[a];
			for (int b = 0; b < tmp.length; b++) {
				System.arraycopy(source[a][b], 0, destination[a][b], 0,
						source[a][b].length);
			}
		}
	}

	public static void multiArrayCopy(double[][][] source,
			double[][][] destination) {
		for (int a = 0; a < source.length; a++) {
			double[][] tmp = source[a];
			for (int b = 0; b < tmp.length; b++) {
				System.arraycopy(source[a][b], 0, destination[a][b], 0,
						source[a][b].length);
			}
		}
	}

	public static double[] arrayListToDoulbe(List<Double> a) {
		int count = a.size();
		double[] result = new double[count];
		for (int i = 0; i < count; i++) {
			result[i] = (Double) a.get(i);
		}
		return result;
	}

	public static String doubleFormat(double value) {
		return common.Static.df4.format(value);
	}

	public static double randomDouble(Random rand, double value1, double value2) {
		return value1 + rand.nextDouble() * (value2 - value1);
	}

	public static double randomDoubleLogScale(Random rand, double value1,
			double value2) {
		double log1 = Math.log(value1);
		double log2 = Math.log(value2);
		return Math.exp(randomDouble(rand, log1, log2));
	}

	public static double avgLogScale(double value1, double value2) {
		double log1 = Math.log(value1);
		double log2 = Math.log(value2);
		return Math.exp((log1 + log2) / 2);
	}

	public static int getFirstPeak(double[] data, int skipTime) {
		for (int i = skipTime + 1; i < data.length - 1; i++) {
			boolean peak = true;
			for (int j = Math.max(0, i - windowSizeForPeakSearching / 2); j < i; j++) {
				if (data[j] >= data[j + 1]) {
					peak = false;
					break;
				}
			}
			for (int j = i; j < Math.min(data.length - 1, i
					+ windowSizeForPeakSearching / 2); j++) {
				if (data[j] <= data[j + 1]) {
					peak = false;
					break;
				}
			}
			if (peak) {
				// System.out.println(i);
				return i;
			}
		}
		return -1;
	}
	
	public static int getLastPeak(double[] data, int skipTime) {
		for (int i = skipTime; i > 0; i--) {
			boolean peak = true;
			for (int j = Math.max(0, i - windowSizeForPeakSearching / 2); j < i; j++) {
				if (data[j] >= data[j + 1]) {
					peak = false;
					break;
				}
			}
			for (int j = i; j < Math.min(data.length - 1, i
					+ windowSizeForPeakSearching / 2); j++) {
				if (data[j] <= data[j + 1]) {
					peak = false;
					break;
				}
			}
			if (peak) {
				// System.out.println(i);
				return i;
			}
		}
		return -1;
	}

	public static int getFirstBottom(double[] data, int skipTime) {
		for (int i = skipTime + 1; i < data.length - 1; i++) {
			boolean peak = true;
			for (int j = Math.max(0, i - windowSizeForPeakSearching / 2); j < i; j++) {
				if (data[j] < data[j + 1]) {
					peak = false;
					break;
				}
			}
			for (int j = i; j < Math.min(data.length - 1, i
					+ windowSizeForPeakSearching / 2); j++) {
				if (data[j] > data[j + 1]) {
					peak = false;
					break;
				}
			}
			if (peak)
				return i;
		}
		return -1;
	}

	public static int getFirstStable(double[] data, int skipTime) {
		int startStable = 0;
		for (int i = Math.max(1, skipTime); i < data.length; i++) {
			if (Math.abs(data[i] - data[i - 1]) < ConstantsMath.changeThresholdStable) {
				if (startStable == 0) {
					startStable = i;
				} else {
					if (i > startStable + ConstantsMath.timeToCheckStability) {
						return startStable;
					}
				}
			} else {
				startStable = 0;
			}
		}
		return -1;
	}

	/*
	 * public static String saveGraph(OligoGraph<Integer, String> graph,
	 * Layout<Integer, String> layout) { DecimalFormat df = new
	 * DecimalFormat("#.##"); DecimalFormat df2 = new DecimalFormat("#.######");
	 * String res = "SEQ\n"; for (Integer v : graph.getVertices()) { if
	 * (layout.getGraph().degree(v) > 0) { res += v + "\t" +
	 * df.format(graph.getC(v)) + "\t" + df.format(layout.transform(v).getX()) +
	 * "\t" + df.format(layout.transform(v).getY()) + "\t" +
	 * df2.format(graph.getK(v)) + "\n"; } } res += "TEM\n"; for (String e :
	 * graph.getEdges()) { res += graph.getSource(e) + "->" + graph.getDest(e) +
	 * "\t" + graph.getSource(e) + "\t" + graph.getDest(e) + "\t" +
	 * df.format(graph.getTC(e)) + "\n"; } res += "INHIB\n"; for (Integer v :
	 * graph.getVertices()) { if (v > 10 && layout.getGraph().degree(v) > 0) {
	 * int inhFrom = v / 10; int inhTo = v % 10; res += v + "\t" + inhFrom +
	 * "->" + inhTo + "\t" + inhFrom + "\t" + inhTo + "\n"; } } return res; }
	 * 
	 * public static String saveGraphNEAT(OligoGraph<String, String> graph,
	 * Layout<String, String> layout, Individual individuals) { DecimalFormat df
	 * = new DecimalFormat("#.##"); DecimalFormat df2 = new
	 * DecimalFormat("#.######"); OligoSystemComplex oligoModel =
	 * individuals.getOligoModel(); String nodeNumbering = "Node numbering:\n";
	 * for (int i : oligoModel.intToName.keySet()) { nodeNumbering += i + "->" +
	 * oligoModel.intToName.get(i) + "\n"; } String res = nodeNumbering +
	 * "SEQ\n"; for (String v : graph.getVertices()) { if
	 * (layout.getGraph().degree(v) > 0) { res += oligoModel.nameToInt.get(v) +
	 * "\t" + df.format(graph.getC(v)) + "\t" +
	 * df.format(layout.transform(v).getX()) + "\t" +
	 * df.format(layout.transform(v).getY()) + "\t" + df2.format(graph.getK(v))
	 * + "\n"; } } res += "TEM\n"; for (String e : graph.getEdges()) { res +=
	 * oligoModel.nameToInt.get(graph.getSource(e)) + "->" +
	 * oligoModel.nameToInt.get(graph.getDest(e)) + "\t" +
	 * oligoModel.nameToInt.get(graph.getSource(e)) + "\t" +
	 * oligoModel.nameToInt.get(graph.getDest(e)) + "\t" +
	 * df.format(graph.getTC(e)) + "\n"; } res += "INHIB\n";
	 * 
	 * for (String v : graph.getVertices()) { if (v.startsWith("I")) { for (Node
	 * tmp1 : individuals.nodeGenes) { for (Node tmp2 : individuals.nodeGenes) {
	 * if (v.equals("I" + tmp1.name + tmp2.name)) { int index1 =
	 * oligoModel.nameToInt.get(tmp1.name); int index2 =
	 * oligoModel.nameToInt.get(tmp2.name); res += oligoModel.nameToInt.get(v) +
	 * "\t" + index1 + "->" + index2 + "\t" + index1 + "\t" + index2 + "\n";
	 * 
	 * } }
	 * 
	 * }
	 * 
	 * } } return res; }
	 */

	public static double distance(boolean[][][] matric1, boolean[][][] matric2) {
		int count = 0;
		int count2 = 0;
		for (int i = 0; i < matric1.length; i++) {
			for (int j = 0; j < matric1[i].length; j++) {
				for (int k = 0; k < matric1[i][j].length; k++) {
					if (matric1[i][j][k] != false || matric2[i][j][k] != false) {
						count++;
						if (matric1[i][j][k] != matric2[i][j][k]) {
							count2++;
						}
					}
				}
			}
		}
		System.out.println(count2 + "/" + count);
		return (double) count2 / count;
	}

	private static int getSeqPosFromString(String str) {
		for (int i = 0; i < Static.seqStr.length; i++) {
			if (Static.seqStr[i].equals(str)) {
				return i;
			}
		}
		return -1;
	}

	public static double calculateInhK(double K1, double K2) {
		return (double) 1 / 100 * Math.exp((Math.log(K1) + Math.log(K2)) / 2);
	}

	public static double calculateInhK(String s1, String s2) {
		return Static.inhibKm[getSeqPosFromString(s1)][getSeqPosFromString(s2)];
	}

	public static double getParamMutationFactor(double max, double min,
			double x, double r) {
		return Math.pow((max / min), r * x);
	}

	public static double gaussianTransform(Random rand, double oldParam,
			double min, double max) {
		double mutatedParam = gaussianTransform(rand, oldParam);
		if (mutatedParam < min)
			mutatedParam = min;
		if (mutatedParam > max)
			mutatedParam = max;

		return mutatedParam;
	}

	public static double gaussianTransformEnzymes(Random rand, double oldParam,
			double min, double max) {
		double mutatedParam = gaussianTransform(rand, oldParam);
		if (mutatedParam < min)
			mutatedParam = min;
		if (mutatedParam > max)
			mutatedParam = max;

		return mutatedParam;
	}

	public static double gaussianTransform(Random rand, double oldParam) {
		double mutatedParam = Math.exp(Math.log(oldParam)
				* (1 + rand.nextGaussian()
						* Constants.paramMutateGaussianFactor))
				+ rand.nextGaussian() * Constants.paramMutateGaussianFactor2;
		return mutatedParam;
	}

	public static double multipliactiveGaussianTransform(Random rand,
			double oldParam) {
		double mutatedParam = Math.exp(Math.log(oldParam)
				* (1 + rand.nextGaussian()
						* Constants.paramMutateGaussianFactor));
		return mutatedParam;
	}

	public static double probDisableConnection(double param) {
		return Math.min(1, Math.max(0,
				Constants.probConnectionDisabling - Math.log(param)));
	}

	public static PlotExpData getSpeciesFitnessPlot(
			ArrayList<ArrayList<Species>> speciesData,
			Map<Individual, Color> indivColor) {
		int totalSpecies = 0;
		Map<Integer, String> intToName = new HashMap<Integer, String>();
		Map<String, Integer> nameToInt = new HashMap<String, Integer>();
		Species[][] species = new Species[speciesData.size()][3];
		Map<Integer, Color> intToColor = new HashMap<Integer, Color>();
		for (int i = 0; i < speciesData.size(); i++) {
			ArrayList<Species> curGenSpecies = speciesData.get(i);
			ArrayList<Species> bestSpecies = new ArrayList<Species>();
			for (int j = 0; j < 3; j++) {
				Species sp = null;
				for (Species spe : curGenSpecies) {
					if (!bestSpecies.contains(spe)) {
						if (sp == null
								|| sp.getBestIndividual().getFitness() < spe
										.getBestIndividual().getFitness()) {
							sp = spe;
						}
					}
				}
				species[i][j] = sp;
				if (sp != null) {
					if (!nameToInt.containsKey(sp.getName())) {
						intToName.put(totalSpecies, sp.getName());
						intToColor.put(totalSpecies,
								indivColor.get(sp.representative));
						nameToInt.put(sp.getName(), totalSpecies);
						totalSpecies++;
					}
					bestSpecies.add(sp);

					/*
					 * System.out.println("Generation " + i + ": rank " + (j +
					 * 1) + ": " + sp.toString());
					 */
				}
			}
		}
		double[][] data = new double[totalSpecies * 2][speciesData.size()];
		for (int i = 0; i < speciesData.size(); i++) {
			for (int j = 0; j < 3; j++) {
				if (species[i][j] != null) {
					data[nameToInt.get(species[i][j].getName())][i] = species[i][j]
							.getBestIndividual().getFitness();
					data[nameToInt.get(species[i][j].getName()) + totalSpecies][i] = species[i][j]
							.getSpeciesFitness();
				}
			}
		}
		String[] names = new String[totalSpecies * 2];
		Color[] color = new Color[totalSpecies * 2];
		Stroke[] stroke = new Stroke[totalSpecies * 2];
		BasicStroke normalStroke = new BasicStroke(1);
		BasicStroke dashStroke = new BasicStroke(1, BasicStroke.CAP_ROUND,
				BasicStroke.JOIN_ROUND, 2, new float[] { 2, 2 }, 2);
		for (int i = 0; i < totalSpecies; i++) {
			names[i] = intToName.get(i);
			names[i + totalSpecies] = intToName.get(i) + "(avg)";
			if (names[i].equals(species[species.length - 1][0].getName())) {
				names[i] += "*";
				names[i + totalSpecies] += "*";
			}
			color[i] = intToColor.get(i);
			color[i + totalSpecies] = intToColor.get(i);
			stroke[i] = normalStroke;
			stroke[i + totalSpecies] = dashStroke;
		}
		return new PlotExpData("Fitness over generations", data, names, color,
				stroke, "Generation", "Fitness");
	}

	public static PlotExpData getGenericSpeciesFitnessPlot(
			ArrayList<ArrayList<optimizers.ganeatgeneric.Species>> speciesData,
			Map<optimizers.ganeatgeneric.Individual, Color> indivColor) {
		int totalSpecies = 0;
		Map<Integer, String> intToName = new HashMap<Integer, String>();
		Map<String, Integer> nameToInt = new HashMap<String, Integer>();
		optimizers.ganeatgeneric.Species[][] species = new optimizers.ganeatgeneric.Species[speciesData
				.size()][3];
		Map<Integer, Color> intToColor = new HashMap<Integer, Color>();
		for (int i = 0; i < speciesData.size(); i++) {
			ArrayList<optimizers.ganeatgeneric.Species> curGenSpecies = speciesData
					.get(i);
			ArrayList<optimizers.ganeatgeneric.Species> bestSpecies = new ArrayList<optimizers.ganeatgeneric.Species>();
			for (int j = 0; j < 3; j++) {
				optimizers.ganeatgeneric.Species sp = null;
				for (optimizers.ganeatgeneric.Species spe : curGenSpecies) {
					if (!bestSpecies.contains(spe)) {
						if (sp == null
								|| sp.getBestIndividual().getFitness() < spe
										.getBestIndividual().getFitness()) {
							sp = spe;
						}
					}
				}
				species[i][j] = sp;
				if (sp != null) {
					if (!nameToInt.containsKey(sp.getName())) {
						intToName.put(totalSpecies, sp.getName());
						intToColor.put(totalSpecies,
								indivColor.get(sp.representative));
						nameToInt.put(sp.getName(), totalSpecies);
						totalSpecies++;
					}
					bestSpecies.add(sp);

					/*
					 * System.out.println("Generation " + i + ": rank " + (j +
					 * 1) + ": " + sp.toString());
					 */
				}
			}
		}
		double[][] data = new double[totalSpecies * 2][speciesData.size()];
		for (int i = 0; i < speciesData.size(); i++) {
			for (int j = 0; j < 3; j++) {
				if (species[i][j] != null) {
					data[nameToInt.get(species[i][j].getName())][i] = species[i][j]
							.getBestIndividual().getFitness();
					data[nameToInt.get(species[i][j].getName()) + totalSpecies][i] = species[i][j]
							.getSpeciesFitness();
				}
			}
		}
		String[] names = new String[totalSpecies * 2];
		Color[] color = new Color[totalSpecies * 2];
		Stroke[] stroke = new Stroke[totalSpecies * 2];
		BasicStroke normalStroke = new BasicStroke(1);
		BasicStroke dashStroke = new BasicStroke(1, BasicStroke.CAP_ROUND,
				BasicStroke.JOIN_ROUND, 2, new float[] { 2, 2 }, 2);
		for (int i = 0; i < totalSpecies; i++) {
			names[i] = intToName.get(i);
			names[i + totalSpecies] = intToName.get(i) + "(avg)";
			if (names[i].equals(species[species.length - 1][0].getName())) {
				names[i] += "*";
				names[i + totalSpecies] += "*";
			}
			color[i] = intToColor.get(i);
			color[i + totalSpecies] = intToColor.get(i);
			stroke[i] = normalStroke;
			stroke[i + totalSpecies] = dashStroke;
		}
		return new PlotExpData("Number of templates over generations", data,
				names, color, stroke, "Generation", "Fitness");
	}

	public static String getShortMutationString(String mutationString) {
		if (mutationString.contains("(-)")) {
			return "Switch";
		}
		if (mutationString.contains("Mutate Parameters")) {
			return "Parameter";
		}
		if (mutationString.contains("Mutate Add Activation")) {
			return "AddActivation";
		}
		if (mutationString.contains("Mutate Add Inhibition")) {
			return "AddInhibition";
		}
		if (mutationString.contains("Mutate Add Node")) {
			return "AddNode";
		}
		System.out.println(mutationString);
		return "?";
	}

	public static void createImage(Component component, String filepath,
			int width, int height) {

		BufferedImage bi = new BufferedImage(width, height,
				BufferedImage.TYPE_INT_ARGB);
		Graphics g = bi.createGraphics();
		component.paint(g); // this == JComponent
		g.dispose();
		try {
			ImageIO.write(bi, "png", new File(filepath));
		} catch (Exception e) {
		}
	}

	public static Element createRunConfigXML(Document doc, RunConfig runConfig) {
		Element result = doc.createElement("optimize");
		Element template = runConfig.template.getXMLElement(doc, "template");
		result.appendChild(template);
		Element sequenceK = runConfig.seqK.getXMLElement(doc, "seqK");
		result.appendChild(sequenceK);
		Element inhK = runConfig.inhK.getXMLElement(doc, "inhK");
		result.appendChild(inhK);
		Element seqC = runConfig.seqC.getXMLElement(doc, "seqC");
		result.appendChild(seqC);
		Element exoAct = runConfig.exoActivity
				.getXMLElement(doc, "exoActivity");
		result.appendChild(exoAct);
		Element exoSKm = runConfig.exoSimpleKm
				.getXMLElement(doc, "exoSimpleKm");
		result.appendChild(exoSKm);
		Element exoInhibKm = runConfig.exoInhibKm.getXMLElement(doc,
				"exoInhibKm");
		result.appendChild(exoInhibKm);
		Element polAct = runConfig.polActivity
				.getXMLElement(doc, "polActivity");
		result.appendChild(polAct);
		Element polKm = runConfig.polKm.getXMLElement(doc, "polKm");
		result.appendChild(polKm);
		Element polDoubleStrandKm = runConfig.polDoubleStrandKm.getXMLElement(
				doc, "polDoubleStrandKm");
		result.appendChild(polDoubleStrandKm);
		Element nickActivity = runConfig.nickActivity.getXMLElement(doc,
				"nickActivity");
		result.appendChild(nickActivity);
		Element nickKm = runConfig.nickKm.getXMLElement(doc, "nickKm");
		result.appendChild(nickKm);
		return result;
	}

	public static Element createModelSpecificationXML(Document doc) {
		Element result = doc.createElement("model");
		Element setting = doc.createElement("saturation_coupling");
		result.appendChild(setting);
		setting.setAttribute("exonuclease_saturation",
				String.valueOf(model.Constants.saturableExonuclease));
		setting.setAttribute("exonuclease_coupling",
				String.valueOf(model.Constants.coupledExonuclease));
		setting.setAttribute("polymerase_saturation",
				String.valueOf(model.Constants.saturablePoly));
		setting.setAttribute("polymerase_coupling",
				String.valueOf(model.Constants.coupledPoly));
		setting.setAttribute("nicking_saturation",
				String.valueOf(model.Constants.saturableNick));
		setting.setAttribute("nicking_coupling",
				String.valueOf(model.Constants.coupledNick));
		setting.setAttribute("exonuclease_saturation_free_templates",
				String.valueOf(model.Constants.exoSaturationByFreeTemplates));

		Element Kduplex = doc.createElement("Kduplex");
		result.appendChild(Kduplex);
		Kduplex.setAttribute("text", "Association speed (Kduplex)");
		Kduplex.setAttribute("value", String.valueOf(model.Constants.Kduplex));
		Element n = doc.createElement("n");
		result.appendChild(n);
		n.setAttribute("text", "Nickase Activity");
		n.setAttribute("value", String.valueOf(model.Constants.n));
		Element p = doc.createElement("p");
		result.appendChild(p);
		p.setAttribute("text", "Polymerase Activity");
		p.setAttribute("value", String.valueOf(model.Constants.p));
		Element lembda = doc.createElement("lembda");
		result.appendChild(lembda);
		lembda.setAttribute("text", "Inhibition Factor of Templates (lembda)");
		lembda.setAttribute("value", String.valueOf(model.Constants.lembda));
		Element exo = doc.createElement("exo");
		result.appendChild(exo);
		exo.setAttribute("text", "Exonuclease Activity");
		exo.setAttribute("value", String.valueOf(model.Constants.exo));
		Element exoInhib = doc.createElement("exoInhib");
		result.appendChild(exoInhib);
		exoInhib.setAttribute("text", "Exonuclease Activity with Inhibition");
		exoInhib.setAttribute("value", String.valueOf(model.Constants.exoInhib));
		Element displ = doc.createElement("displ");
		result.appendChild(displ);
		displ.setAttribute("text", "Displacement Slowdown");
		displ.setAttribute("value", String.valueOf(model.Constants.displ));
		Element exoVm = doc.createElement("exoVm");
		result.appendChild(exoVm);
		exoVm.setAttribute("text", "exoVm");
		exoVm.setAttribute("value", String.valueOf(model.Constants.exoVm));
		Element exoKmSimple = doc.createElement("exoKmSimple");
		result.appendChild(exoKmSimple);
		exoKmSimple.setAttribute("text", "For Simple Sequence (exoKmSimple)");
		exoKmSimple.setAttribute("value",
				String.valueOf(model.Constants.exoKmSimple));
		Element exoKmInhib = doc.createElement("exoKmInhib");
		result.appendChild(exoKmInhib);
		exoKmInhib.setAttribute("text", "For Inhibiting Sequence (exoKmInhib)");
		exoKmInhib.setAttribute("value",
				String.valueOf(model.Constants.exoKmInhib));
		Element exoKmTemplate = doc.createElement("exoKmTemplate");
		result.appendChild(exoKmTemplate);
		exoKmTemplate.setAttribute("text",
				"Exonuclease saturation by free templates (exoKmTemplate)");
		exoKmTemplate.setAttribute("value",
				String.valueOf(model.Constants.exoKmTemplate));
		Element polKm = doc.createElement("polKm");
		result.appendChild(polKm);
		polKm.setAttribute("text", "polKm");
		polKm.setAttribute("value", String.valueOf(model.Constants.polKm));
		Element nickVm = doc.createElement("nickVm");
		result.appendChild(nickVm);
		nickVm.setAttribute("text", "nickVm");
		nickVm.setAttribute("value", String.valueOf(model.Constants.nickVm));
		Element leakRatio = doc.createElement("leakRatio");
		result.appendChild(leakRatio);
		leakRatio.setAttribute("text", "Leak Ratio");
		leakRatio.setAttribute("value",
				String.valueOf(model.Constants.leakRatio));
		Element toeholdLeft = doc.createElement("toeholdLeft");
		result.appendChild(toeholdLeft);
		toeholdLeft.setAttribute("text", "Toehold Left");
		toeholdLeft.setAttribute("value",
				String.valueOf(model.Constants.ratioToeholdLeft));
		Element toeholdRight = doc.createElement("toeholdRight");
		result.appendChild(toeholdRight);
		toeholdRight.setAttribute("text", "Toehold Right");
		toeholdRight.setAttribute("value",
				String.valueOf(model.Constants.ratioToeholdRight));
		Element polVm = doc.createElement("polVm");
		result.appendChild(polVm);
		polVm.setAttribute("text", "polVm");
		polVm.setAttribute("value", String.valueOf(model.Constants.polVm));
		Element polKmBoth = doc.createElement("polKmBoth");
		result.appendChild(polKmBoth);
		polKmBoth.setAttribute("text", "polKmBoth");
		polKmBoth.setAttribute("value",
				String.valueOf(model.Constants.polKmBoth));
		Element nickKm = doc.createElement("nickKm");
		result.appendChild(nickKm);
		nickKm.setAttribute("text", "nickKm");
		nickKm.setAttribute("value", String.valueOf(model.Constants.nickKm));
		Element ratioInhibition = doc.createElement("ratioInhibition");
		result.appendChild(ratioInhibition);
		ratioInhibition.setAttribute("text", "Ratio Inhibition");
		ratioInhibition.setAttribute("value",
				String.valueOf(model.Constants.ratioInhibition));
		Element maxTime = doc.createElement("maxTime");
		result.appendChild(maxTime);
		maxTime.setAttribute("text", "Max time (complex model)");
		maxTime.setAttribute("value", String.valueOf(model.Constants.maxTime));
		Element numberOfPoints = doc.createElement("numberOfPoints");
		result.appendChild(numberOfPoints);
		numberOfPoints.setAttribute("text", "Number of points");
		numberOfPoints.setAttribute("value",
				String.valueOf(model.Constants.numberOfPoints));
		Element maxTimeSimple = doc.createElement("maxTimeSimple");
		result.appendChild(maxTimeSimple);
		maxTimeSimple.setAttribute("text", "Max time (simple model)");
		maxTimeSimple.setAttribute("value",
				String.valueOf(model.Constants.maxTimeSimple));
		Element integrationAbsoluteError = doc
				.createElement("integrationAbsoluteError");
		result.appendChild(integrationAbsoluteError);
		integrationAbsoluteError.setAttribute("text",
				"Integration absolute error");
		integrationAbsoluteError.setAttribute("value",
				String.valueOf(model.Constants.integrationAbsoluteError));
		Element integrationRelativeError = doc
				.createElement("integrationRelativeError");
		result.appendChild(integrationRelativeError);
		integrationRelativeError.setAttribute("text",
				"Integration relative error");
		integrationRelativeError.setAttribute("value",
				String.valueOf(model.Constants.integrationRelativeError));
		Element changeThresholdStable = doc
				.createElement("changeThresholdStable");
		result.appendChild(changeThresholdStable);
		changeThresholdStable.setAttribute("text",
				"Threshold for stability detection");
		changeThresholdStable.setAttribute("value", String
				.valueOf(optimizers.commons.GAConstants.changeThresholdStable));
		Element minTimeStable = doc.createElement("minTimeStable");
		result.appendChild(minTimeStable);
		minTimeStable.setAttribute("text", "Min time for stability detection");
		minTimeStable.setAttribute("value",
				String.valueOf(optimizers.commons.GAConstants.minTimeStable));
		Element maxIntegrationSteps = doc.createElement("maxIntegrationSteps");
		result.appendChild(maxIntegrationSteps);
		maxIntegrationSteps.setAttribute("text", "Max integration step");
		maxIntegrationSteps.setAttribute("value", String
				.valueOf(optimizers.commons.GAConstants.maxIntegrationSteps));
		return result;
	}

	public static double[] sampleEntropy(double[] y, int M, double r) {
		int n = y.length;
		int[] lastrun = new int[n];
		int[] run = new int[n];
		double[] A = new double[M];
		double[] B = new double[M];
		double[] p = new double[M];
		double[] e = new double[M];
		for (int i = 0; i < n - 1; i++) {
			int nj = n - i;
			double y1 = y[i];
			for (int jj = 0; jj < nj; jj++) {
				int j = jj + i;
				if (Math.abs(y[j] - y1) < r) {
					run[jj] = lastrun[jj] + 1;
					int M1 = Math.min(M, run[jj]);
					for (int m = 0; m < M1; m++) {
						A[m] = A[m] + 1;
						if (j < n) {
							B[m] = B[m] + 1;
						}
					}
				} else {
					run[jj] = 0;
				}
			}
			for (int j = 0; j < nj; j++) {
				lastrun[j] = run[j];
			}
		}
		int N = n * (n - 1) / 2;
		p[0] = A[0] / N;
		e[0] = -Math.log(p[0]);
		for (int m = 1; m < M; m++) {
			p[m] = A[m] / B[m - 1];
			e[m] = -Math.log(p[m]);
		}
		return e;
	}

	public static void swapPanels(JPanel oldPanel, JPanel newPanel) {
		int x = oldPanel.getX();
		int y = oldPanel.getY();
		boolean visible = oldPanel.isVisible();
		oldPanel.setLocation(newPanel.getX(), newPanel.getY());
		oldPanel.setVisible(!visible);
		newPanel.setLocation(x, y);
		newPanel.setVisible(visible);
	}

	public static ArrayList<int[]> getSets(int nLib, int solutionSize) {
		ArrayList<int[]> result = new ArrayList<int[]>();
		int[] solution = new int[solutionSize];
		getInputs(0, nLib, solutionSize, solution, result);
		return result;
	}

	private static void getInputs(int pos, int nLib, int solutionSize,
			int[] solution, ArrayList<int[]> result) {
		for (int i = 0; i < nLib; i++) {
			boolean found = false;
			for (int j = 0; j < pos; j++) {
				if (i == solution[j]) {
					found = true;
					break;
				}
			}
			if (!found) {
				solution[pos] = i;
			} else {
				continue;
			}
			if (pos == solutionSize - 1) {
				result.add(solution.clone());
			} else {
				getInputs(pos + 1, nLib, solutionSize, solution, result);
			}
		}

	}

	public static ArrayList<double[]> getTestsForMath(double min, double max,
			int n) {
		ArrayList<double[]> result = new ArrayList<double[]>();
		double step = (max - min) / (n - 1);
		double[] inputs = new double[ConstantsMath.nInput];
		getInputs(0, step, n, min, inputs, result);
		return result;
	}

	public static ArrayList<double[]> getTestsForMathLogScale(double min,
			double max, int n) {
		ArrayList<double[]> result = new ArrayList<double[]>();
		double step = (Math.log(max) - Math.log(min)) / (n - 1);
		double[] inputs = new double[ConstantsMath.nInput];
		getInputsLogScale(0, step, n, Math.log(min), inputs, result);
		/*
		 * for (int i = 0; i < result.size(); i++) {
		 * System.out.print(result.get(i)[0] + ", "); } result = new
		 * ArrayList<double[]>(); double[] tmp = new double[] { 1.0,
		 * 1.021275801581022, 1.043004262894959, 1.0651950146404723,
		 * 1.0878578924170568, 1.1110029410844708, 1.134640419214916,
		 * 1.15878080363994, 1.1834347940940806, 1.208613317957304,
		 * 1.2343275350983443, 1.2605888428210885, 1.2874088809162,
		 * 1.3147995368202188, 1.3427729508844253, 1.3713415217558058,
		 * 1.400517911872499, 1.4303150530761657, 1.460746152343763,
		 * 1.4918246976412703, 1.765477253877561, 2.0893272104203744,
		 * 2.4725825170589957, 2.9261401819563364, 3.462896103723912,
		 * 4.0981117374797025, 4.849848020218827, 5.739478893195253,
		 * 6.792299022175846, 8.038242994734095, 9.512736443351331,
		 * 11.25770329410874, 13.322757779835833, 15.766615109945324,
		 * 18.658760906198765, 22.08142687107802, 26.131928862477718,
		 * 30.925433852647487, 36.598234443677974, 37.37689121791741,
		 * 38.172114539185266, 38.984256874049024, 39.813678188084864,
		 * 40.660746105425225, 41.52583607170057, 42.40933152044812,
		 * 43.31162404306096, 44.23311356235295, 45.17420850981637,
		 * 46.135326006650935, 47.11689204864421, 48.1193416949856,
		 * 49.14311926109751, 50.18867851556913, 51.25648288128006,
		 * 52.34700564080323, 53.460730146177596, 54.598150033144236 }; for (int
		 * i = 0; i < tmp.length; i++) { result.add(new double[] { tmp[i] }); }
		 */
		return result;
	}

	private static void getInputs(int firstPos, double step, int n, double min,
			double[] inputs, ArrayList<double[]> result) {
		for (int i = 0; i < n; i++) {
			inputs[firstPos] = min + i * step;
			if (firstPos == inputs.length - 1) {
				result.add(inputs.clone());
			} else {
				getInputs(firstPos + 1, step, n, min, inputs, result);
			}
		}
	}

	private static void getInputsLogScale(int firstPos, double step, int n,
			double min, double[] inputs, ArrayList<double[]> result) {
		for (int i = 0; i < n; i++) {
			inputs[firstPos] = Math.exp(min + i * step);
			if (firstPos == inputs.length - 1) {
				result.add(inputs.clone());
			} else {
				getInputsLogScale(firstPos + 1, step, n, min, inputs, result);
			}
		}
	}

	public static double[] getInputsForFCD(double min, double max, int n) {
		double logmin = Math.log(min);
		double logmax = Math.log(max);
		double step = (logmax - logmin) / (n - 1);
		double[] result = new double[n];
		double tmp = min;
		for (int i = 0; i < result.length - 1; i++) {
			double current = Math.exp(logmin + step * (i + 1));
			result[i + 1] = current - tmp;
			tmp = current;
		}
		result[0] = 0;
		return result;
	}

	public static String arrayToString(double[] array) {
		StringBuilder builder = new StringBuilder();
		builder.append("[");
		for (int i = 0; i < array.length - 1; i++) {
			builder.append(Static.df2.format(array[i]) + ",");
		}
		builder.append(Static.df2.format(array[array.length - 1]));
		builder.append("]");
		return builder.toString();
	}

	public static double strToDouble(String seqStr) {
		double start = Math.log(Static.seqK[0]);
		double length = Math.log(Static.seqK[Static.seqK.length - 1]) - start;
		for (int i = 0; i < Static.seqStr.length; i++) {
			if (seqStr.equals(Static.seqStr[i])) {
				double param = Math.log(Static.seqK[i]);
				return (param - start) / length;
			}
		}
		return 0;
	}

	public static int[] doubleToIntSet(double[] params) {
		double start = Math.log(Static.seqK[0]);
		double length = Math.log(Static.seqK[Static.seqK.length - 1]) - start;
		int[] result = new int[params.length];
		double[][] distances = new double[params.length][Static.seqK.length];
		for (int i = 0; i < params.length; i++) {
			for (int j = 0; j < Static.seqK.length; j++) {
				double param = params[i] * length + start;
				distances[i][j] = Math.abs(param
						- Math.log(Static.seqKForDE[j]));
			}
		}
		boolean[] paramsFixed = new boolean[params.length];
		boolean[] seqFixed = new boolean[Static.seqK.length];
		for (int k = 0; k < params.length; k++) {
			int bestI = 0, bestJ = 0;
			double bestDistance = Double.MAX_VALUE;
			for (int i = 0; i < params.length; i++) {
				for (int j = 0; j < Static.seqK.length; j++) {
					if (bestDistance > distances[i][j] && !paramsFixed[i]
							&& !seqFixed[j]) {
						bestDistance = distances[i][j];
						bestI = i;
						bestJ = j;
					}
				}
			}
			distances[bestI][bestJ] = Double.MAX_VALUE;
			paramsFixed[bestI] = true;
			seqFixed[bestJ] = true;
			result[bestI] = bestJ;
		}
		return result;
	}

	public static double MSE(double[] arr1, double[] arr2) {
		double result = 0;
		for (int i = 0; i < arr1.length; i++) {
			double tmp = arr1[i] - arr2[i];
			result = result + tmp * tmp;
		}
		return result / arr1.length;
	}

	public static void main(String[] args) {
		MyUtil.getTestsForMathLogScale(1, Math.exp(0.4), 20);
		MyUtil.getTestsForMathLogScale(Math.exp(0.4), Math.exp(3.6), 20);
		MyUtil.getTestsForMathLogScale(Math.exp(3.6), Math.exp(4), 20);
	}
}
